#include <stdio.h>
#include <string.h>
#include "max7219_transfer.h"

#define MAX7219_SPI_HOST SPI2_HOST

#define LOG_TAG "MAX7219_TRANSFER"
#define MAX7219_MAX_CLOCK_SPEED_HZ (10000000) // 10 MHz

#define ALL_CHIPS 0xff

#define REG_DIGIT_0      (1 << 8)
#define REG_DECODE_MODE  (9 << 8)
#define REG_INTENSITY    (10 << 8)
#define REG_SCAN_LIMIT   (11 << 8)
#define REG_SHUTDOWN     (12 << 8)
#define REG_DISPLAY_TEST (15 << 8)

#define VAL_CLEAR_BCD    0x0f
#define VAL_CLEAR_NORMAL 0x00

static inline uint16_t shuffle(uint16_t val)
{
    return (val >> 8) | (val << 8);
}

// 此函数一次传输一行，可以根据chip_index指定显示的模块，和从入线开始的下标一样
// 比如，接线处开始为0，chip_index选择0，那么就是在第一个模块刷信息，原理是用0填充了。
// 但是这样的话，如果用buffer(比如四个模块串联，整个屏幕开一个缓存)做缓存，
// 用这个函数效率就比较低，因为每次发送都会用0填充其他的模块。
// 这个函数比较时候初始化指令数据这类，或者只是做简单显示
// 如果要做复杂的，比如字体压缩、动画等等，则推荐使用transfer_send_buffer函数
static esp_err_t send(max7219_tranfer * handle, uint8_t chip_index, uint16_t value)
{
    if (handle == NULL) ESP_LOGE(LOG_TAG, "handle error!\n");
    uint16_t buf[handle->cascade_size];
    memset(buf, 9, sizeof(buf));
    if (chip_index == ALL_CHIPS)
    {
        for (uint8_t i = 0; i < handle->cascade_size; i++)
            buf[i] = shuffle(value);
    }
    else buf[chip_index] = shuffle(value);

    spi_transaction_t t;
    memset(&t, 0, sizeof(t));
    t.length = handle->cascade_size * 16;
    t.tx_buffer = buf;
    return spi_device_transmit(handle->spi_dev, &t);
}

esp_err_t max7219_clear(max7219_tranfer *handle)
{
    if (handle == NULL) ESP_LOGE(LOG_TAG, "handle error!\n");
    uint8_t val = handle->bcd ? VAL_CLEAR_BCD : VAL_CLEAR_NORMAL;
    for (uint8_t i = 0; i < 8; i++)
        ESP_ERROR_CHECK(send(handle, ALL_CHIPS, (REG_DIGIT_0 + ((uint16_t)i << 8)) | val));

    return ESP_OK;
}

esp_err_t max7219_set_decode_mode(max7219_tranfer *handle, bool bcd)
{
    if (handle == NULL) ESP_LOGE(LOG_TAG, "handle error!\n");
    handle->bcd = bcd;
    ESP_ERROR_CHECK(send(handle, ALL_CHIPS, REG_DECODE_MODE | (bcd ? 0xff : 0)));
    ESP_ERROR_CHECK(max7219_clear(handle));

    return ESP_OK;
}

esp_err_t max7219_set_shutdown_mode(max7219_tranfer *handle, bool shutdown)
{
    if (handle == NULL) ESP_LOGE(LOG_TAG, "handle error!\n");
    ESP_ERROR_CHECK(send(handle, ALL_CHIPS, REG_SHUTDOWN | !shutdown));
    return ESP_OK;
}

/***************************************************************************************/

esp_err_t tranfer_init(max7219_tranfer * handle)
{
    if (handle == NULL) ESP_LOGE(LOG_TAG, "handle error!\n");

    // Init IDF SPI
    spi_bus_config_t cfg = {
        .mosi_io_num = handle->mosi_pin,
        .miso_io_num = -1,
        .sclk_io_num = handle->clk_pin,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 0,
        .flags = 0
    };
    ESP_ERROR_CHECK(spi_bus_initialize(handle->used_spi_id, &cfg, SPI_DMA_CH_AUTO));

    spi_device_interface_config_t spi_cfg =
    {
        .spics_io_num = handle->cs_pin,
        .clock_speed_hz = MAX7219_MAX_CLOCK_SPEED_HZ,
        .mode = 0,
        .queue_size = 1,
        .flags = SPI_DEVICE_NO_DUMMY
    };
    ESP_ERROR_CHECK(spi_bus_add_device(handle->used_spi_id, &spi_cfg, &handle->spi_dev));

    // init the display

    // Shutdown all chips
    ESP_ERROR_CHECK(max7219_set_shutdown_mode(handle, true));
    // Disable test
    ESP_ERROR_CHECK(send(handle, ALL_CHIPS, REG_DISPLAY_TEST));
    // Set max scan limit
    ESP_ERROR_CHECK(send(handle, ALL_CHIPS, REG_SCAN_LIMIT | (8 - 1))); //一共八行，八个寄存器，所以写8
    // Set normal decode mode & clear display
    ESP_ERROR_CHECK(max7219_set_decode_mode(handle, false));
    // Set minimal brightness
    ESP_ERROR_CHECK(transfer_set_brightness(handle, 0));
    // Wake up
    ESP_ERROR_CHECK(max7219_set_shutdown_mode(handle, false));

    return ESP_OK;
}

esp_err_t transfer_send_buffer(max7219_tranfer * handle)
{
    if (handle == NULL) ESP_LOGE(LOG_TAG, "handle error!\n");
    esp_err_t return_value;
    uint8_t (* matrix_buffer)[handle->cascade_size] = (uint8_t(*)[handle->cascade_size])handle->matrix_buffer;
    // 8 行，刷新八次
    for(int i = 1; i < 9; i++) 
    {
        uint16_t trans_block_buf[handle->cascade_size];
        memset(trans_block_buf, 0, sizeof(trans_block_buf));
        for(int j = 0; j < handle->cascade_size; j++){
            uint16_t trans_block = ((uint16_t)i<<8 | ((matrix_buffer)[i-1][j]));
            trans_block_buf[j] = shuffle(trans_block);
            // printf("the value :%x\n", trans_block_buf[j]);
        }

        spi_transaction_t t;
        memset(&t, 0, sizeof(t));
        t.length = handle->cascade_size * 16; // 单位bit
        t.tx_buffer = trans_block_buf;
        return_value = spi_device_transmit(handle->spi_dev, &t);
    }
    return return_value;
}

esp_err_t transfer_send_buffer_custom(max7219_tranfer * handle, void * buffer)
{
    if (handle == NULL) ESP_LOGE(LOG_TAG, "handle error!\n");
    esp_err_t return_value;
    uint8_t (* matrix_buffer)[handle->cascade_size] = (uint8_t(*)[handle->cascade_size])buffer;
    // 8 行，刷新八次
    for(int i = 1; i < 9; i++) 
    {
        uint16_t trans_block_buf[handle->cascade_size];
        memset(trans_block_buf, 0, sizeof(trans_block_buf));
        for(int j = 0; j < handle->cascade_size; j++){
            uint16_t trans_block = ((uint16_t)i<<8 | ((matrix_buffer)[i-1][j]));
            trans_block_buf[j] = shuffle(trans_block);
            // printf("the value :%x\n", trans_block_buf[j]);
        }

        spi_transaction_t t;
        memset(&t, 0, sizeof(t));
        t.length = handle->cascade_size * 16; // 单位bit
        t.tx_buffer = trans_block_buf;
        return_value = spi_device_transmit(handle->spi_dev, &t);
    }
    return return_value;
}

esp_err_t transfer_set_brightness(max7219_tranfer *handle, uint8_t value)
{
    if (handle == NULL) ESP_LOGE(LOG_TAG, "handle error!\n");
    if (value > MAX7219_MAX_BRIGHTNESS) {
        ESP_LOGE(LOG_TAG, "brightness too high!\n");
        value = MAX7219_MAX_BRIGHTNESS;
    }
    ESP_ERROR_CHECK(send(handle, ALL_CHIPS, REG_INTENSITY | value));
    return ESP_OK;
}

